// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "ChartCreation.h" // 包含图表创建相关头文件
#include "LyraPerformanceStatTypes.h" // 包含Lyra性能统计类型
#include "Algo/MaxElement.h" // 包含算法：查找最大元素
#include "Algo/MinElement.h" // 包含算法：查找最小元素
#include "Stats/StatsData.h" // 包含统计数据头文件
#include "Subsystems/GameInstanceSubsystem.h" // 包含游戏实例子系统基类

#include "LyraPerformanceStatSubsystem.generated.h" // 包含由UHT生成的头文件

enum class ELyraDisplayablePerformanceStat : uint8; // 前置声明：可显示的性能统计指标枚举

class FSubsystemCollectionBase; // 前置声明：子系统集合基类
class ULyraPerformanceStatSubsystem; // 前置声明：Lyra性能统计子系统类
class UObject; // 前置声明：UObject类
struct FFrame; // 前置声明：帧结构

/**
 * 存储给定样本大小的缓冲区，并提供接口来获取该组数据的最小值、最大值和平均值等。
 */
class FSampledStatCache
{
public:

	/**
	 * 构造函数。
	 * @param InSampleSize 样本大小，必须大于0。
	 */
	FSampledStatCache(const int32 InSampleSize = 125)
		: SampleSize(InSampleSize) // 初始化样本大小
	{
		check(InSampleSize > 0); // 确保样本大小大于0
		
		Samples.Empty(); // 清空样本数组
		Samples.AddZeroed(SampleSize); // 为样本数组添加 SampleSize 个零初始化元素
	}
		
	/**
	 * 记录一个样本值。
	 * @param Sample 要记录的样本值。
	 */
	void RecordSample(const double Sample)
	{
		// 一个简单的环形缓冲区，用于随时间存储样本
		Samples[CurrentSampleIndex] = Sample; // 将样本值存储到当前索引位置
	
		CurrentSampleIndex++; // 当前索引递增
		if (CurrentSampleIndex >= Samples.Num()) // 如果当前索引超出样本数组范围
		{
			CurrentSampleIndex = 0u; // 重置当前索引为0，实现环形缓冲
		}
	}

	/**
	 * 获取当前缓存的统计值（即最新写入的值，注意：环形缓冲区中此位置的值可能已被后续写入覆盖，取决于调用时机）。
	 * @return 当前索引位置的样本值。
	 */
	double GetCurrentCachedStat() const
	{
		return Samples[CurrentSampleIndex]; // 返回当前索引位置的样本值
	}
		
	/**
	 * 获取最后缓存的统计值（即上一次写入的值）。
	 * @return 上一次写入的样本值。
	 */
	double GetLastCachedStat() const
	{
		int32 LastIndex = CurrentSampleIndex - 1; // 计算上一个索引
		if (LastIndex < 0) // 如果上一个索引小于0
		{
			LastIndex = Samples.Num() - 1; // 则上一个索引为数组最后一个元素
		}
			
		return Samples[LastIndex]; // 返回上一个索引位置的样本值
	}

	/**
	 * 遍历所有当前样本，从最近采样的索引开始。
	 * @param Func 对每个样本值执行的回调函数。
	 */
	void ForEachCurrentSample(const TFunctionRef<void(const double Stat)> Func) const
	{
		int32 Index = CurrentSampleIndex; // 从当前索引开始
	
		for (int32 i = 0; i < SampleSize; i++) // 遍历所有样本
		{
			Func(Samples[Index]); // 对当前样本值调用回调函数
				
			Index++; // 索引递增
			if (Index == SampleSize) // 如果索引等于样本大小（即到达数组末尾）
			{
				Index = 0; // 重置索引为0，实现环形遍历
			}
		}
	}

	/**
	 * 获取样本大小。
	 * @return 样本大小。
	 */
	inline int32 GetSampleSize() const
	{
		return SampleSize; // 返回样本大小
	}

	/**
	 * 计算样本的平均值。
	 * @return 样本的平均值。
	 */
	inline double GetAverage() const
	{
		double Sum = 0.0; // 初始化总和为0
		ForEachCurrentSample([&Sum](const double Stat) // 遍历所有样本
		{
			Sum += Stat; // 将每个样本值累加到总和
		});

		// 返回平均统计值
		return Sum / static_cast<double>(SampleSize); // 总和除以样本数量
	}

	/**
	 * 查找样本中的最小值。
	 * @return 样本中的最小值。
	 */
	inline double GetMin() const
	{
		return *Algo::MinElement(Samples); // 使用算法库查找并返回最小值
	}

	/**
	 * 查找样本中的最大值。
	 * @return 样本中的最大值。
	 */
	inline double GetMax() const
	{
		return *Algo::MaxElement(Samples); // 使用算法库查找并返回最大值
	}
		
private:
	const int32 SampleSize = 125; // 样本大小，常量

	int32 CurrentSampleIndex = 0; // 当前样本索引，用于环形缓冲区
	
	TArray<double> Samples; // 存储样本值的数组
};

//////////////////////////////////////////////////////////////////////

// 观察者，缓存上一帧的统计信息
struct FLyraPerformanceStatCache : public IPerformanceDataConsumer // 继承自性能数据消费者接口
{
public:
	/**
	 * 构造函数。
	 * @param InSubsystem 指向所属的 ULyraPerformanceStatSubsystem 的指针。
	 */
	FLyraPerformanceStatCache(ULyraPerformanceStatSubsystem* InSubsystem)
		: MySubsystem(InSubsystem) // 初始化所属子系统
	{
	}

	//~IPerformanceDataConsumer 接口开始
	virtual void StartCharting() override; // 开始图表记录
	virtual void ProcessFrame(const FFrameData& FrameData) override; // 处理帧数据
	virtual void StopCharting() override; // 停止图表记录
	//~IPerformanceDataConsumer 接口结束

	/**
	* 返回给定统计类型的最新缓存值。
	* @param Stat 要查询的性能统计指标。
	* @return 该统计指标的最新缓存值。
	*/
	double GetCachedStat(ELyraDisplayablePerformanceStat Stat) const;

	/**
	 * 返回给定统计类型的缓存数据指针。这可用于获取该统计信息的最小值/最大值/平均值、最新统计信息以及迭代所有样本。
	 * 这对于生成一些UI（例如随时间变化的FPS图表）非常有用。
	 * @param Stat 要查询的性能统计指标。
	 * @return 指向该统计指标缓存数据的常量指针，如果未找到则返回 nullptr。
	 */
	const FSampledStatCache* GetCachedStatData(const ELyraDisplayablePerformanceStat Stat) const;

protected:

	/**
	 * 记录一个性能统计值。
	 * @param Stat 要记录的统计指标。
	 * @param Value 统计值。
	 */
	void RecordStat(const ELyraDisplayablePerformanceStat Stat, const double Value);
	
	ULyraPerformanceStatSubsystem* MySubsystem; // 指向所属子系统的指针

	/**
	 * 缓存当前可用的每个性能统计指标的采样数据。
	 */
	TMap<ELyraDisplayablePerformanceStat, FSampledStatCache> PerfStateCache; // 性能状态缓存映射
};

//////////////////////////////////////////////////////////////////////

// 子系统，允许访问性能统计信息以用于显示目的
UCLASS(BlueprintType) // UCLASS 宏，标记为可在蓝图中使用
class ULyraPerformanceStatSubsystem : public UGameInstanceSubsystem // 继承自游戏实例子系统
{
	GENERATED_BODY() // 生成反射体代码

public:
	/**
	 * 蓝图可调用函数：获取指定性能统计指标的缓存值。
	 * @param Stat 要查询的性能统计指标。
	 * @return 该统计指标的缓存值。
	 */
	UFUNCTION(BlueprintCallable)
	double GetCachedStat(ELyraDisplayablePerformanceStat Stat) const;

	/**
	 * 获取指定性能统计指标的缓存数据。
	 * @param Stat 要查询的性能统计指标。
	 * @return 指向该统计指标缓存数据的常量指针。
	 */
	const FSampledStatCache* GetCachedStatData(const ELyraDisplayablePerformanceStat Stat) const;

	//~USubsystem 接口开始
	virtual void Initialize(FSubsystemCollectionBase& Collection) override; // 初始化子系统
	virtual void Deinitialize() override; // 反初始化子系统
	//~USubsystem 接口结束

protected:
	
	TSharedPtr<FLyraPerformanceStatCache> Tracker; // 性能统计跟踪器的共享指针
};